//
// CommandLineParser.cpp
// Utility class that helps in parsing command line options.
//
// Author: Gabriel H. Barbuto (gbarbuto@dc.uba.ar)
//
#include <iostream>

#include <boost/program_options.hpp>

#include "CommandLineParser.h"
#include "MethodFactory.h"
#include "Program.h"

namespace po = boost::program_options;

CommandLineParser::CommandLineParser(Program &program) :
_program(program)
{
}

int CommandLineParser::parse() const
{
	po::variables_map vm;
	po::options_description description(this->build_options_description());

	try
	{
		po::store(po::command_line_parser(
			this->_program._argc, this->_program._argv).options(description).run(), vm);

		if (vm.empty() || vm.count("help"))
		{
			std::cout << description << std::endl;

			return Program::USAGE_SHOWN;
		}

		po::notify(vm);
	}
	catch (const po::error &e)
	{
		std::cerr << "ERROR: " << e.what() << std::endl << std::endl;
		std::cerr << description << std::endl;

		return Program::ERROR_IN_COMMAND_LINE;
	}

	std::string method_name(this->_program._method_name);
	std::string function_name(this->_program._function_name);

	if (method_name != MethodFactory::BISECTION_METHOD_NAME &&
		method_name != MethodFactory::NEWTON_METHOD_NAME &&
		method_name != MethodFactory::SECANT_METHOD_NAME)
	{
		std::cerr << "ERROR: Invalid method name." << std::endl << std::endl;
		return Program::ERROR_IN_COMMAND_LINE;
	}

	if (function_name != MethodFactory::DIRECT_FUNCTION_NAME &&
		function_name != MethodFactory::INVERSE_FUNCTION_NAME)
	{
		std::cerr <<  "ERROR: Invalid function name." << std::endl << std::endl;
		return Program::ERROR_IN_COMMAND_LINE;
	}

	return Program::SUCCESS;
}

po::options_description CommandLineParser::build_options_description() const
{
	po::options_description description("Program Options");

	description.add_options()
		("alpha",		po::value<double>(&(this->_program._alpha))->required(),
						"the value of alpha.")

		("arguments",	po::value<std::vector<double>>(&(this->_program._arguments))->multitoken()->required(),
						"method initial value(s).")

		("help",		"Print help message.")

		("epsilon",		po::value<double>(&(this->_program._epsilon)),
						"the epsilon the selected method will use.")

		("function",	po::value<std::string>(&(this->_program._function_name))->required(),
						"the function to use [direct | inverse].")

		("iterations",	po::value<std::size_t>(&(this->_program._iterations)),
						"the maximum number of iterations the method will perform.")

		("method",		po::value<std::string>(&(this->_program._method_name))->required(),
						"the name of the method [bisection | newton | secant].")
		;

	return description;
}
